﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Chapter11
{
    using System.Threading;

    partial class Program
    {
        internal static void Begin(Action action, bool active = false)
        {
            if (active)
            {
                Console.WriteLine("开始演示：");
                action();
                Console.WriteLine("演示完毕：");
                Console.ReadLine();
            }
        }

        private class NewMailEventArgs : EventArgs
        {
            private readonly string from, to, subject;

            internal NewMailEventArgs(string from, string to, string subject)
            {
                this.from = from;
                this.to = to;
                this.subject = subject;
            }

            internal string Subject
            {
                get { return subject; }
            }

            internal string To
            {
                get { return to; }
            }

            internal string From
            {
                get { return from; }
            }
        }

        private class MailManager
        {
            internal event EventHandler<NewMailEventArgs> NewMail;

            protected virtual void OnNewMail(NewMailEventArgs e)
            {
                e.Raise(this, ref NewMail);
            }

            internal void SimulateNewMail(string from, string to, string subject)
            {
                NewMailEventArgs e = new NewMailEventArgs(from, to, subject);
                OnNewMail(e);
            }
        }

        private sealed class Fax
        {
            internal Fax(MailManager mm)
            {
                mm.NewMail += FaxMsg;
            }

            private void FaxMsg(object sender, NewMailEventArgs e)
            {
                Console.WriteLine("Faxing mail message:");
                Console.WriteLine(" From={0}, To={1}, Subject={2}", e.From, e.To, e.Subject);
            }
        }

        private sealed class EventKey : Object { }

        private sealed class EventSet
        {
            private readonly Dictionary<EventKey, Delegate> events = new Dictionary<EventKey, Delegate>();

            internal void Add(EventKey eventKey, Delegate handler)
            {
                lock (events)
                {
                    Delegate d;
                    events.TryGetValue(eventKey, out d);
                    events[eventKey] = Delegate.Combine(d, handler);
                }
            }

            internal void Remove(EventKey eventKey, Delegate handler)
            {
                lock (events)
                {
                    Delegate d;
                    if (events.TryGetValue(eventKey, out d))
                    {
                        d = Delegate.Remove(d, handler);
                        if (d != null)
                            events[eventKey] = d;
                        else
                            events.Remove(eventKey);
                    }
                }
            }

            internal void Raise(EventKey eventKey, object sender, EventArgs e)
            {
                Delegate d;
                lock (events)
                {
                    events.TryGetValue(eventKey, out d);
                }
                if (d != null)
                {
                    d.DynamicInvoke(new object[] { sender, e });
                }
            }
        }

        private class FooEventArgs : EventArgs { }

        private class TypeWithLotsOfEvents
        {
            private readonly EventSet eventSet = new EventSet();

            protected EventSet EventSet { get { return eventSet; } }

            protected static readonly EventKey fooEventKey = new EventKey();

            internal event EventHandler<FooEventArgs> Foo
            {
                add { eventSet.Add(fooEventKey, value); }
                remove { eventSet.Remove(fooEventKey, value); }
            }

            protected virtual void OnFoo(FooEventArgs e)
            {
                eventSet.Raise(fooEventKey, this, e);
            }

            internal void SimulateFoo()
            {
                OnFoo(new FooEventArgs());
            }
        }

        internal static void C04()
        {
            TypeWithLotsOfEvents twle = new TypeWithLotsOfEvents();
            twle.Foo += HandleFooEvent;
            twle.SimulateFoo();
        }

        private static void HandleFooEvent(object sender, FooEventArgs e)
        {
            Console.WriteLine("Handleing Foo Event here...");
        }
    }

    internal static class EventArgsExtensions
    {
        internal static void Raise<TEventArgs>(this TEventArgs e, object sender, ref EventHandler<TEventArgs> eventDelegate)
            where TEventArgs : EventArgs
        {
            EventHandler<TEventArgs> temp = Interlocked.CompareExchange(ref eventDelegate, null, null);
            if (temp != null)
                temp(sender, e);
        }
    }
}
